package com.hd.trans.utils;


import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseBooleanArray;

import com.hd.trans.framework.tools.ThreadPoolWrapper;
import com.hd.trans.network.HeaderInterceptor;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;

public enum DomainManager {
    INSTANCE;

    private OkHttpClient client;

    /**
     * 域名枚举类，预定义现有接口域名及其备用域名，对域名进行分类
     */
    public enum Domain {
        app_xunjiepdf_com,
        xunjieoss_xunjiepdf_com,
        api_shoujihuifu_com,
        user_api_hudunsoft_com,
        voice_xunjiepdf_com,
        tj_huduntech_com,
        kuaishouapi_yiyongcad_com,
        xiaomaai_nnxieli_com,
        api_xunjietupian_com
    }

    /**
     * 域名测试监听
     */
    public interface OnDomainTestFinishListener {

        /**
         * 【子线程】域名测试结束回调<br />
         * 【注意】：此方法在非UI线程中被调用
         */
        void onFinish();
    }

    private final SparseArray<ArrayList<String>> mDomains;// 根据Domain枚举分类存储所有域名及其备用域名
    private final ArrayList<Domain> mDomainsTarget;// 存储研发传入的待检测可用性的域名(Domain枚举分类)
    private final ConcurrentHashMap<Integer,Pair<String, Boolean>> mDomainsTested;// 存储每个枚举分类的域名检测结果,线程安全
    private final HashMap<String, Long> mDomainsUrls;
    private boolean isHttps;// 使用http还是https。true时，https；false时，http
    private String productId;// 产品ID

    DomainManager() {
        mDomains = new SparseArray<>();
        mDomainsTarget = new ArrayList<>();
        mDomainsTested = new ConcurrentHashMap<>();
        mDomainsUrls = new HashMap<>();
        isHttps = true;

        /**
         * 域名添加规则<br />
         * 1、主域名位于第一位<br />
         * 2、备用域名从第二位开始不分顺序<br />
         * 3、最后一位为x-machineid检验码，不需要校验码时，设置为空字符串即可
         */

        // 域名 #1
        mDomains.append(Domain.app_xunjiepdf_com.ordinal(), new ArrayList<>(Arrays.asList(
                "app.xunjiepdf.com",
                "app2.weiduruanjian.com",
                "app2.pptbest.com",
                "20-201"
        )));

        // 域名 #2
        mDomains.append(Domain.xunjieoss_xunjiepdf_com.ordinal(), new ArrayList<>(Arrays.asList(
                "xunjieoss.xunjiepdf.com",
                "xunjieoss2.weiduruanjian.com",
                "xunjieoss2.pptbest.com",
                "20-201"
        )));

        // 域名 #3
        mDomains.append(Domain.api_shoujihuifu_com.ordinal(), new ArrayList<>(Arrays.asList(
                "h5.daotudashi.com",
                "api2.weiduruanjian.com",
                "api2.pptbest.com",
                "20-201"
        )));

        // 域名 #4
        mDomains.append(Domain.user_api_hudunsoft_com.ordinal(), new ArrayList<>(Arrays.asList(
                "user.api.hudunsoft.com",
                "user2.weiduruanjian.com",
                "user2.pptbest.com",
                "20-201"
        )));

        // 域名 #5
        mDomains.append(Domain.voice_xunjiepdf_com.ordinal(), new ArrayList<>(Arrays.asList(
                "voice.xunjiepdf.com",
                "voice2.weiduruanjian.com",
                "voice2.pptbest.com",
                "20-201"
        )));

        // 域名 #6
        mDomains.append(Domain.tj_huduntech_com.ordinal(), new ArrayList<>(Arrays.asList(
                "tj.huduntech.com",
                "tj2.weiduruanjian.com",
                "tj2.pptbest.com",
                ""
        )));

        // 域名 #7
        mDomains.append(Domain.kuaishouapi_yiyongcad_com.ordinal(), new ArrayList<>(Arrays.asList(
                "kuaishouapi.yiyongcad.com",
                "kuaishouapi2.weiduruanjian.com",
                "kuaishouapi2.pptbest.com",
                ""
        )));

        // 域名 #8
        mDomains.append(Domain.xiaomaai_nnxieli_com.ordinal(), new ArrayList<>(Arrays.asList(
                "xiaomaai.nnxieli.com",
                "xiaomaai.weiduruanjian.com",
                "xiaomaai.pptbest.com",
                "20-201"
        )));

        // 域名 #9
        mDomains.append(Domain.api_xunjietupian_com.ordinal(), new ArrayList<>(Arrays.asList(
                "iconvert-api.xunjietupian.com",
                "iconvert-api.weiduruanjian.com",
                "iconvert-api.pptbest.com",
                "20-201"
        )));
    }

    /**
     * 添加待检测的域名枚举类
     * @param domains 域名枚举类，每种类型对应具有相同功能的多个域名，可以传入一个或多个域名枚举类
     */
    public void setDomainTargets(Domain... domains) {
        mDomainsTarget.clear();
        Collections.addAll(mDomainsTarget, domains);
    }

    /**
     * 设置域名检测使用http还是https
     * @param https true，使用https，false，使用http，默认为true
     */
    public void setHttps(boolean https) {
        isHttps = https;
    }

    /**
     * 设置产品ID
     * @param productId 产品ID，后台要求传入，用于数据分析
     */
    public void setProductId(String productId) {
        this.productId = productId;
    }

    /**
     * 开始域名检测
     * @param listener 域名检测结果回调，在这个回调中，调用{getDomainUrl}获取可用域名
     */
    @Deprecated
    public void startDomainTest(OnDomainTestFinishListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //final SparseBooleanArray state = new SparseBooleanArray();
                final ArrayList<Domain> domainList = new ArrayList<>(mDomainsTarget);
                for (Domain d: domainList) {
                    if(isHasDomain(d)){//已检查过
                        continue;
                    }
                    final ArrayList<String> domains = mDomains.get(d.ordinal());
                    final String verify = domains.get(domains.size() - 1);
                    final boolean needHealth = needHealth(d.ordinal());
                    for (int i = 0; i < domains.size() - 1; ++i) {
                        final String domain = (isHttps ? "https://" : "http://") + domains.get(i);
                        if (isDomainValid2(domain, productId, verify, needHealth)) {
                            mDomainsTested.put(d.ordinal(), new Pair<>(domain, true));
                            break;
                        }
                    }
                }

                if (null != listener) {
                    listener.onFinish();
                }
            }
        }).start();
    }

    /**
     * 异步开始域名检测,多线程运行
     * @param listener
     */
    public void startDomainTestAsyn(OnDomainTestFinishListener listener) {
        new Thread(() -> {
            final ArrayList<Domain> domainList = new ArrayList<>(mDomainsTarget);
            CountDownLatch countDownLatch = new CountDownLatch(domainList.size());
            for (Domain d: domainList) {
                ThreadPoolWrapper.getInstance().excuseDownloadThread(new MyRunnable(d,countDownLatch));
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (null != listener) {
                listener.onFinish();
            }
        }).start();
    }

    private class MyRunnable implements Runnable{
        private Domain d;
        private CountDownLatch countDownLatch;
        public MyRunnable(Domain d, CountDownLatch countDownLatch) {
            this.d = d;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            if(!isHasDomain(d)){//未检查过
                final ArrayList<String> domains = mDomains.get(d.ordinal());
                final String verify = domains.get(domains.size() - 1);
                final boolean needHealth = needHealth(d.ordinal());
                for (int i = 0; i < domains.size() - 1; ++i) {
                    final String domain = (isHttps ? "https://" : "http://") + domains.get(i);
                    if (isDomainValid2(domain, productId, verify, needHealth)) {
                        mDomainsTested.put(d.ordinal(), new Pair<>(domain, true));
                        break;
                    }
                }
            }
            countDownLatch.countDown();
        }
    }

    private boolean needHealth(int ordinal){
        if(Domain.tj_huduntech_com.ordinal() == ordinal){
            //tj_huduntech_com中的域名无法添加消息头health
            return false;
        }
        return true;
    }

    /**
     * 根据域名枚举类，获取此类型域名中的可用域名<br />
     * 【注意】：如果某域名枚举类对应的所有域名都不可用，此方法将返回主域名。
     * @param domain 域名枚举类
     * @return 域名url及是否可用的Pair。first值为域名url，其结构为：[http/https + 域名]，second为域名是否可用。当所有域名都不可用时，此值为false，first将返回主域名
     */
    public Pair<String, Boolean> getDomainUrl(Domain domain) {
        Pair<String, Boolean> result = mDomainsTested.get(domain.ordinal());
        if (null == result) {
            //如果没有检测过，返回主域名
            result = new Pair<>((isHttps ? "https://" : "http://") + mDomains.get(domain.ordinal()).get(0), false);
        }
        return result;
    }

    public boolean isHasDomain(Domain domain){
        Pair<String, Boolean> result = mDomainsTested.get(domain.ordinal());
        return null != result;
    }

    /**
     * 判断域名是否可用
     * @param domain 域名
     * @param verify x-machineid校验码
     * @return true，可用，false，不可用
     */
    private boolean isDomainValid(String domain, String productId, String verify) {
        String urlString = domain + "/health";
        HttpURLConnection connection = null;
        try {
            URL url = new URL(urlString);
            connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(30 * 1000);
            connection.setRequestMethod("HEAD");
            connection.setRequestProperty("X-Product", productId);
            connection.setRequestProperty("X-Domain", url.getHost());
            connection.connect();

            final int code = connection.getResponseCode();
            if (verify.isEmpty()) {
                if (200 == code) {
                    return true;
                }
            } else {
                final String machineid = connection.getHeaderField("x-machineid");
                return 200 == code && null != machineid && machineid.equals(verify);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != connection) {
                connection.disconnect();
            }
        }
        return false;
    }


    private boolean isDomainValid2(String domain, String productId, String verify, boolean needHealth) {
        if(mDomainsUrls.containsKey(domain)){
            if(System.currentTimeMillis() - mDomainsUrls.get(domain) < 5000){
                return false;
            }
        }
        mDomainsUrls.put(domain,System.currentTimeMillis());
        //
        String urlString;
        if(needHealth){
            urlString = domain + "/health";
        }else {
            urlString = domain + "/";
        }
        Request request = new  Request.Builder()
                .url(urlString)
                //.addHeader("X-Product", productId)
                .head()
                .build();
        // new client对象时注意配置的链接与超时时间，根据需要进行调整
        if(client == null){
            client = new OkHttpClient.Builder()
                    .connectTimeout(3, TimeUnit.SECONDS)
                    .readTimeout(3, TimeUnit.SECONDS)
                    .addInterceptor(new HttpLoggingInterceptor())
                    .addInterceptor(new HeaderInterceptor(true))
                    .build();
        }

        try{
            Response response = client.newCall(request).execute();
            final int code = response.code();
            if (verify.isEmpty()) {
                if (200 == code) {
                    return true;
                }
            } else {
                final String machineid = response.header("x-machineid","");
                return 200 == code && null != machineid && machineid.equals(verify);
            }
        }catch (Exception e){
            //e.printStackTrace();
        }
        return false;
    }
}
